package Server;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.apache.commons.digester3.Digester;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.IndexWriterConfig.OpenMode;
import org.apache.lucene.index.Term;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

public class Indexer {

	public Indexer() throws ParserConfigurationException, SAXException, IOException {
		String path = "data/data";
		File file = new File(path);

		String indexPath = "index";

		File indexDir = new File(indexPath);
		boolean shouldUpdate = indexDir.exists();

		Directory dir = FSDirectory.open(new File(indexPath));
		Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_40);
		IndexWriterConfig iwc = new IndexWriterConfig(Version.LUCENE_40, analyzer);

		if (!shouldUpdate) {
			iwc.setOpenMode(OpenMode.CREATE);
		} else {
			iwc.setOpenMode(OpenMode.CREATE_OR_APPEND);
		}

		iwc.setRAMBufferSizeMB(256.0);

		final IndexWriter writer = new IndexWriter(dir, iwc);
		
		// Write documents to the index as they are pulled from XML

		SAXParserFactory factory = SAXParserFactory.newInstance();
		SAXParser saxParser = factory.newSAXParser();

		DefaultHandler handler = new DefaultHandler() {

			private String currentElement;
			private HashMap<String, String> doc;
			private Document document;

			public void startElement(String uri, String localName, String qName,
					Attributes attributes) throws SAXException {
				// At the start of a doc create a new Document & HashMap
				if (qName.equalsIgnoreCase("DOC")) {
					doc = new HashMap<String, String>();
					document = new Document();
				}
				currentElement = qName;
			}

			public void endElement(String uri, String localName, String qName) throws SAXException {

				// At the end of a doc pull every field out of the HashMap
				// And add it to the Document
				try {
					if (qName.equalsIgnoreCase("DOC")) {
						for (String fieldName : doc.keySet()) {
							String value = cleanValue(fieldName, doc.get(fieldName));
							Field field = new TextField(fieldName.trim(), value.trim(),
									Field.Store.YES);
							document.add(field);
						}
						if (writer.getConfig().getOpenMode() == OpenMode.CREATE) {
							writer.addDocument(document);
							System.out.println("Add Document: " + document.get("DOCNO"));
						} else {
							writer.updateDocument(new Term("docNo", document.get("DOCNO")),
									document);
							System.out.println("Updating Document: " + document.get("DOCNO"));
						}
						doc = null;
						document = null;
					}
				} catch (IOException e) {
					e.printStackTrace();
				}
			}

			public void characters(char ch[], int start, int length) throws SAXException {
				String value = new String(ch, start, length);

				if (doc == null)
					return;

				// Text field is a wrapper around most fields in a document. It only contains other fields
				if (currentElement == null || currentElement.equalsIgnoreCase("text"))
					return;

				// If the HashMap already contains a value for this element append the new value to it.
				// This parser used stops at new lines & whitespace so the characters method can be called 
				// several times for the currentElement
				if (doc.containsKey(currentElement)) {
					// Append to current value
					String current = doc.remove(currentElement).concat(value);
					doc.put(currentElement, current);
				} else {
					doc.put(currentElement, value);
				}

			}

		};

		saxParser.parse(file, handler);

		writer.close();
		
		System.out.println("Done!");
	}
	
	/**
	 * Trim the element name from the beginning of the value
	 * @param element
	 * @param value
	 * @return
	 */
	private String cleanValue(String element, String value) {
		if(value.startsWith(element)) {
			value = value.replaceFirst(element, "");
		}
		if(value.startsWith(":")) {
			value = value.replaceFirst(":", "");
		}
		return value;
	}

	public static void main(String[] args) {
		try {
			new Indexer();
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

}
